﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

using Azure.Logger;

namespace Azure.Core
{
    public class AzNewDataBlock
    {
        public class AzDataCell
        {
            public String ColumnName { get; set; }
            public Type ColumnType { get; set; }
            public Object CellValue { get; set; }
        }

        public String DataSource { get; set; }
        public Dictionary<String, AzDataCell> DataCells { get; set; }

        public AzNewDataBlock()
        {
            DataCells = new Dictionary<String, AzDataCell>();
        }
    }

    internal class AzNewTaskContent
    {
        public String URI { get; set; }
        public String WhichParser { get; set; }
    }

    internal class AzParser
    {
        public delegate void NewTaskFoundDelegate(AzParser sender, List<AzNewTaskContent> newTaskList);
        public delegate void NewDataBlockFoundDelegate(AzParser sender, List<AzNewDataBlock> dataList);
        public delegate void RaiseErrorHandlingDelegate(AzParser sender, AzErrorHandling errorHandling);
        public delegate void ParseCompleteDelegate(AzParser sender);

        private String _name = String.Empty;
        private List<AzUriTarget> _uriTargetList = null;
        private List<AzBlockTarget> _blockTargetList = null;

        public String Name { get { return _name; } }
        public List<AzUriTarget> UriTargetList { get { return _uriTargetList; } }
        public List<AzBlockTarget> BlockTargetList { get { return _blockTargetList; } }

        public event NewTaskFoundDelegate NewTaskFoundEvent;
        public event NewDataBlockFoundDelegate NewDataBlockFoundEvent;
        public event RaiseErrorHandlingDelegate RaiseErrorHandlingEvent;
        public event ParseCompleteDelegate ParseCompleteEvent;

        public AzParser(String name)
        {
            _name = name;
            _uriTargetList = new List<AzUriTarget>();
            _blockTargetList = new List<AzBlockTarget>();
        }

        //克隆一个新的Parser,除了UriTargetList,其他都指向原来的对象
        //因为这些对象都是只读的，且没有状态改变，所以可以共用
        //而UriTargetList有currentErrorHandlingIndex,这个值会改变，来标识
        //错误处理的状态
        public AzParser Clone()
        {
            AzParser ret = new AzParser(this._name);

            //阴影拷贝BlockTarget
            foreach (AzBlockTarget blkTarget in this._blockTargetList)
            {
                ret._blockTargetList.Add(blkTarget);
            }

            //深拷贝UriTarget
            AzUriTarget[] uriTargetArray = this._uriTargetList.ToArray();

            foreach (AzUriTarget uriTarget in uriTargetArray)
            {
                ret._uriTargetList.Add(uriTarget);
            }

            return ret;
        }

        public void AddUriTarget(AzUriTarget target)
        {
            _uriTargetList.Add(target);
        }

        public void AddBlockTarget(AzBlockTarget target)
        {
            _blockTargetList.Add(target);
        }

        //该函数将在线程池中的线程上运行
        public void BeginParse(AzParseContent content)
        {
            String html = content.HTML;
            List<AzNewTaskContent> taskUriList = new List<AzNewTaskContent>();
            List<AzNewDataBlock> newDataList = new List<AzNewDataBlock>();

            //先分析UriTarget
            foreach (AzUriTarget uriTarget in this._uriTargetList)
            {
                AzFormula formula = uriTarget.Formula;

                Regex regex = new Regex(formula.Regex);
                MatchCollection mc = regex.Matches(html);

                //没有匹配到Uri
                if (mc.Count == 0)
                {
                    AzErrorHandling eh = uriTarget.GetCurrentErrorHandling();

                    if (eh == null)
                    {
                        AzLogger.Error(this._name + ":" + uriTarget.Name + "在处理" + content.URI + "时没有找到ErrorHandling，将使用stopparse作为默认操作");
                        eh = new AzErrorHandling(ErrorHandlingOperation.STOPPARSE, String.Empty);
                    }

                    //异步通知，保证线程不会外部阻塞，但不需要等待结果
                    AzLogger.Warn(this._name + ":" + uriTarget.Name + "在处理" + content.URI + "时没有匹配成功，将使用" + eh.Operation.ToString() + "来处理该异常，参数为" + eh.Param);
                    RaiseErrorHandlingEvent.BeginInvoke(this, eh, null, null);
                    continue;
                }
                else
                {
                    //遍历每个匹配到的
                    foreach (Match m in mc)
                    {
                        //遍历GroupName获取新的url地址
                        StringBuilder newTaskUri = new StringBuilder();
                        newTaskUri.Append(formula.Prefix);

                        if (formula.GroupNameList.Count == 0)
                        {
                            newTaskUri.Append(m.Value);
                        }
                        else
                        {
                            foreach (String groupName in formula.GroupNameList)
                            {
                                if (m.Groups[groupName].Value != String.Empty)
                                    newTaskUri.Append(m.Groups[groupName].Value + " ");
                            }
                        }

                        AzNewTaskContent newTask = new AzNewTaskContent();
                        newTask.WhichParser = uriTarget.ParserName;
                        newTask.URI = newTaskUri.ToString();

                        taskUriList.Add(newTask);

                        //只取第一个MATCH的
                        if (formula.UniqueMatch)
                            break;
                    }
                }//end if (mc.Count == 0)
            }//end foreach (AzUriTarget uriTarget in this._uriTargetList)

            //找到了URI，同步通知订阅此事件的对象
            AzLogger.Info("Parser = " + this._name + "在处理" + content.URI + "时找到了 " + taskUriList.Count + "个新任务");
            NewTaskFoundEvent(this, taskUriList);

            /***************************************************************/

            //再分析BlockTarget
            foreach (AzBlockTarget blkTarget in this._blockTargetList)
            {
                AzNewDataBlock newData = new AzNewDataBlock();
                newData.DataSource = blkTarget.Name;

                foreach (AzTarget target in blkTarget.TargetList)
                {
                    AzFormula formula = target.Formula;
                    AzNewDataBlock.AzDataCell cell = new AzNewDataBlock.AzDataCell();
                    cell.ColumnName = target.Name;
                    cell.ColumnType = target.Type;

                    Regex regex = new Regex(formula.Regex);
                    Match m = regex.Match(html);

                    if (!m.Success)
                    {
                        //使用默认值
                        AzLogger.Warn(this._name + ":" + target.Name + "在处理" + content.URI + "时没有匹配成功");
                        cell.CellValue = target.DefaultValue;
                    }
                    else
                    {
                        StringBuilder strValue = new StringBuilder();
                        strValue.Append(formula.Prefix);
                        //遍历每个group name
                        if (m.Groups.Count == 0)
                        {
                            strValue.Append(m.Value);
                        }
                        else
                        {
                            foreach (String groupName in formula.GroupNameList)
                            {
                                if (m.Groups[groupName].Value != String.Empty)
                                    strValue.Append(m.Groups[groupName].Value + " ");
                            }
                        }

                        target.ParseValue(strValue.ToString());
                        cell.CellValue = target.Value;
                    }//end if (!m.Success)

                    newData.DataCells.Add(target.Name, cell);
                }//end foreach (AzTarget target in blkTarget.TargetList)

                newDataList.Add(newData);
            }//end foreach (AzBlockTarget blkTarget in this._blockTargetList)

            if (newDataList.Count != 0)
            {
                //同步通知其他订阅该事件的对象，找到了一组数据
                AzLogger.Info("Parser = " + this._name + "在处理" + content.URI + "时找到了 " + newDataList.Count + "个新数据块");
                NewDataBlockFoundEvent(this, newDataList);
            }

            //处理完成，通知Robot可以销毁该parser了
            ParseCompleteEvent(this);
        }
    }
}
